package gui;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;

import main.SerialFrame;

import org.toves.checklua.Checker;
import org.toves.checklua.CheckerResult;
import org.toves.checklua.ast.Complaint;
import org.toves.checklua.scan.Token;

import serial.SerialException;
import serial.SerialInterface;

@SuppressWarnings("serial")
public class SendPanel extends Box implements ActionListener {
	private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	private Checker checker;
	private SerialFrame frame;
	private SerialInterface serial;
	private JLabel label;
	private JButton sendProgram;
	private JButton sendUnplug;
	private JButton sendAnyway;
	private Component sendAnywaySep;
	private String sendText;
	private String sendDescript;
	private CheckerResult sendResult;
	
	public SendPanel(SerialFrame frame, SerialInterface serial) {
		super(BoxLayout.LINE_AXIS);
		
		this.checker = new Checker();
		this.frame = frame;
		this.serial = serial;
		this.label = new JLabel("Enter command or program.");
		this.sendProgram = new JButton("Send Program");
		this.sendUnplug = new JButton("Send & Unplug");
		this.sendAnyway = new JButton("Send Anyway");

		add(sendProgram);
		add(createHorizontalStrut(5));
		add(sendUnplug);
		add(createHorizontalStrut(15));
		add(sendAnyway);
		sendAnywaySep = createHorizontalStrut(5);
		add(sendAnywaySep);
		add(label);
		sendAnywaySep.setVisible(false);
		sendAnyway.setVisible(false);
		
		sendProgram.addActionListener(this);
		sendUnplug.addActionListener(this);
		sendAnyway.addActionListener(this);
	}

	public boolean send(String msg, String descript, boolean userCode, boolean disconnect) {
		if (msg.trim().equals("")) {
			setMessage("No " + descript + " to send.");
			return false;
		} else {
			CheckerResult result = checker.process(msg);
			String err = null;
			for (Complaint complaint : result.getComplaints()) {
				err = convertComplaint(complaint, msg);
				break;
			}
			
			if (userCode) {
				setMessage("Logging " + descript + "...");
				Logger.add("--SEND at " + DATE_FORMAT.format(new Date()) + "\n");
				if (err == null) {
					Logger.add("--SEND ok\n");
				} else {
					Logger.add("--SEND msg " + err + "\n");
				}
				if (msg.endsWith("\n")) {
					Logger.add(msg);
				} else {
					Logger.add(msg + "\n");
				}
			}
			
			if (err == null || !userCode) {
				result.commit();
				return doSend(msg, descript, disconnect);
			} else {
				sendText = msg;
				sendResult = result;
				sendDescript = descript;
				setMessage(err);
				sendAnywaySep.setVisible(true);
				sendAnyway.setVisible(true);
				return false;
			}
		}
	}
	
	private String convertComplaint(Complaint complaint, String code) {
		int newline = code.indexOf('\n');
		boolean multiline = newline >= 0 && newline < code.length() - 1;
		
		Token token = complaint.getRepresentativeToken();
		String loc;
		if (multiline) {
			loc = token.getLine() + "." + token.getColumn();
		} else {
			loc = "" + token.getColumn();
		}
		return loc + " \"" + token.getText(10) + "\": " + complaint.getMessage();
	}
	
	public void setMessage(String msg) {
		sendAnywaySep.setVisible(false);
		sendAnyway.setVisible(false);
		label.setText(msg);
	}

	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if (source == sendProgram) {
			frame.sendProgram(false);
		} else if (source == sendUnplug) {
			frame.sendProgram(true);
		} else if (source == sendAnyway) {
			String msg = sendText;
			String descript = sendDescript;
			sendResult.commit();
			sendText = null;

			if (msg != null) {
				Logger.add("--SEND force " + DATE_FORMAT.format(new Date()) + "\n");
				if (msg.endsWith("\n")) {
					Logger.add(msg);
				} else {
					Logger.add(msg + "\n");
				}

				doSend(msg, descript, false);
			}
		}
	}

	private boolean doSend(String msg, String descript, boolean disconnect) {
		setMessage("Sending " + descript + "...");
		try {
			serial.send(msg, disconnect);
			setMessage("Sent " + descript + ".");
			return true;
		} catch (SerialException e) {
			e.printStackTrace();
			setMessage("Error while sending " + descript + ".");
			return false;
		}
	}
}